// Copyright [2022] <Tianyu Zhu, Shizhen Zhao, Zihang Ren>

#ifndef GRAPH_H_
#define GRAPH_H_

#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

using LinkId = int;
using NodeId = int;
using FlowId = int;

const int kMaxValue = 10000000;

struct Link
{
    Link(LinkId link_id_in, NodeId source_id_in, NodeId ter_id_in,
         double cost_in, int bandwidth_in, double delay_in, int srlg_num_in)
        : link_id(link_id_in), source_id(source_id_in),
          ter_id(ter_id_in), cost(cost_in), bandwidth(bandwidth_in),
          delay(delay_in), srlg_num(srlg_num_in)
    {
        weight = 0;
        used = false;
    }
    ~Link()
    {
    }
    LinkId link_id;
    NodeId source_id;
    NodeId ter_id;
    double cost;
    int bandwidth;
    double delay;
    int srlg_num;
    std::vector<int> srlgs;
    std::vector<Link *> cousin;
    int weight;
    bool used;
};

struct Path
{
    Path()
    {
        cost = kMaxValue;
        delay = kMaxValue;
        path_info.reserve(50);
    }

    void clear()
    {
        path_info.clear();
        cost = kMaxValue;
        delay = kMaxValue;
    }

    void Print() const;

    bool find(NodeId id)
    {
        if (node_set.empty())
        {
            for (NodeId node : path_info)
            {
                node_set.insert(node);
            }
        }
        return (node_set.find(id) != node_set.end());
    }

    friend bool operator<(const Path &a, const Path &b)
    {
        return a.cost < b.cost;
    }
    friend bool operator>(const Path &a, const Path &b)
    {
        return a.cost > b.cost;
    }

    std::vector<NodeId> path_info;
    std::vector<Link *> path_link;
    double cost;
    double delay;

private:
    std::unordered_set<NodeId> node_set;
};

struct PathPair
{
    Path ap_path;
    Path bp_path;

    void clear()
    {
        ap_path.clear();
        bp_path.clear();
    }

    void Print() const
    {
        ap_path.Print();
        bp_path.Print();
    }
};

class Graph
{
public:
    explicit Graph(const std::string &file_path);

    int NodeSize() const
    {
        return size_;
    }

    int LinkSize() const
    {
        return links_.size();
    }

    void SortLinks();

    const std::vector<Link *> &GetEgressLinks(
        NodeId node) const
    {
        return node_to_egress_links_.at(node);
    }
    const std::vector<Link *> &GetBpEgressLinks(
        NodeId node) const
    {
        return bp_node_to_egress_links_.at(node);
    }
    const std::vector<Link *> &GetIngressLinks(
        NodeId node) const
    {
        return node_to_ingress_links_.at(node);
    }
    // TODO 可以继续优化这部分的时间 把path的更新融合到bp_egress的初始化中
    void update_bp_egress_links(
        Path path)
    {
        for (Link *link : path.path_link)
        {
            int loc = -1;
            int egress_size = bp_node_to_egress_links_[link->source_id].size();
            for (int i = 0; i < egress_size; ++i)
            {
                Link *tmp = bp_node_to_egress_links_[link->source_id][i];
                if (bp_node_to_egress_links_[link->source_id][i]->link_id == link->link_id)
                {
                    loc = i;
                    break;
                }
            }
            if (loc != 0)
                swap_link_loc(bp_node_to_egress_links_[link->source_id][loc], bp_node_to_egress_links_[link->source_id][0]);
            if(loc == -1)
                std::cout << "error, no such link" << std::endl;
        }
    }

    void ChangeLinkStatus(int srlg_id, bool flag)
    {
        for (int i = 0; i < srlg_group_[srlg_id].size(); ++i)
        {
            srlg_group_[srlg_id][i]->used = flag;
        }
    }
    Link *GetSrlgGroupElement(int srlg_id, int loc)
    {
        return srlg_group_[srlg_id][loc];
    }
    int GetSrlgGroupSize(int srlg_id) const
    {
        return srlg_group_[srlg_id].size();
    }
    std::vector<Link> &GetMutableLinks()
    {
        return links_;
    }

    Link *FindLink(int from, int to)
    {
        std::vector<Link *> &tmp = node_to_egress_links_.at(from);
        for (int i = 0; i < tmp.size(); ++i)
        {
            if (tmp[i]->ter_id == to)
                return tmp[i];
        }
        return nullptr;
    }

private:
    void swap_link_loc(Link *a, Link *b);
    void find_cousin_link(Link *link, std::vector<Link *> link_group);
    bool check_is_cousin_link_pair(const Link *a, const Link *b);

private:
    std::vector<Link> links_;
    std::vector<Link> bp_links_;
    std::unordered_set<NodeId> nodes_;
    std::vector<std::vector<Link *>> node_to_egress_links_;
    std::vector<std::vector<Link *>> node_to_ingress_links_;
    std::vector<std::vector<Link *>> bp_node_to_egress_links_;
    std::vector<std::vector<Link *>> srlg_group_;
    int size_;
    Graph(const Graph &) = delete;
    Graph &operator=(const Graph &) = delete;
};

struct Flow
{
    Flow() {}
    Flow(FlowId id_in, NodeId from_in, NodeId to_in,
         double lb, double ub, int bandwidth_in, bool is_diff_in,
         double diff_in, int type_in, double opt_cost_in) : id(id_in), from(from_in), to(to_in), delay_lb(lb),
                                                            delay_ub(ub), bandwidth(bandwidth_in), is_diff(is_diff_in),
                                                            diff(diff_in), type(type_in), opt_cost(opt_cost_in) {}
    void Print() const;
    FlowId id;
    NodeId from;
    NodeId to;
    double delay_lb;
    double delay_ub;
    int bandwidth;
    bool is_diff;
    double diff;
    int type;
    double opt_cost;
};

class Demand
{
public:
    // Read network flows from a file.
    explicit Demand(const std::string &file_path);

    int NumFlows()
    {
        return flows_.size();
    }
    const Flow &GetFlow(FlowId id)
    {
        return flows_.at(id);
    }

private:
    std::vector<Flow> flows_;
};

#endif // GRAPH_H_
